#pragma once
#include "stdafx.h"
#include "Resource.h"
#pragma comment(lib,"lua5.1.lib")
#pragma comment(lib,"lua51.lib")

extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

class Component;

template<class T> class Luna {
public:
	static void Register(lua_State *L) {
		lua_pushcfunction(L, &Luna<T>::constructor);
		lua_setglobal(L, T::className);

		luaL_newmetatable(L, T::className);
		lua_pushstring(L, "__gc");
		lua_pushcfunction(L, &Luna<T>::gc_obj);
		lua_settable(L, -3);
	}

	static int constructor(lua_State *L) {
		T* obj = new T(L);
		Luna<LuaScript::LuaInterface>::m_mine=obj;
		lua_newtable(L);
		lua_pushnumber(L, 0);
		T** a = (T**)lua_newuserdata(L, sizeof(T*));
		*a = obj;
		luaL_getmetatable(L, T::className);
		lua_setmetatable(L, -2);
		lua_settable(L, -3); // table[0] = obj;

		for (int i = 0; T::Register[i].name; i++) {
			lua_pushstring(L, T::Register[i].name);
			lua_pushnumber(L, i);
			lua_pushcclosure(L, &Luna<T>::thunk, 1);
			lua_settable(L, -3);
		}
		return 1;
	}

	static int thunk(lua_State *L) {
		int i = (int)lua_tonumber(L, lua_upvalueindex(1));
		lua_pushnumber(L, 0);
		lua_gettable(L, 1);

		T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
		lua_remove(L, -1);
		return ((*obj)->*(T::Register[i].mfunc))(L);
	}

	static int gc_obj(lua_State *L) {
		T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
		delete (*obj);
		return 0;
	}

	struct RegType {
		const char *name;
		int(T::*mfunc)(lua_State*);
	};
	static T* m_mine; 
};

class Script : public Component
{
public:

	Script(string);
	~Script(void);
protected:
	string m_scriptName;
};

class LuaScript : public Script
{
public:
	class LuaInterface
	{
	public:
		LuaInterface(lua_State* L) {}
		~LuaInterface() {}
		int LoadModel(lua_State* L);
		int AddRenderObject(lua_State* L);
		static const char className[];
		static const Luna<LuaInterface>::RegType Register[];
		void SetParent(LuaScript* p)
		{
			m_parent=p;
		}
	private:
		LuaScript* m_parent;
	};

	LuaScript(string);
	~LuaScript();
	void AddedToObject();
	void RunScript(const Message&);
	void Initial(const Message&);
	void InitialCallBackClass();
	void InitialLibrary();
	static void AddLuaInterface(LuaInterface*);
private:
	lua_State* m_lua;
	string m_scriptPath;
};